Skip to content

Comments

feat: User feedback and crash reporting system (#113)#158

Merged
deucebucket merged 5 commits intodevelopfrom
feature/issue-113-feedback-system
Feb 16, 2026
Merged

feat: User feedback and crash reporting system (#113)#158
deucebucket merged 5 commits intodevelopfrom
feature/issue-113-feedback-system

Conversation

@deucebucket
Copy link
Owner

Summary

  • New library_manager/feedback.py module with session action logger (circular buffer of 50 actions), path/data sanitizer, and local feedback storage (feedback.json, capped at 200 entries)
  • POST /api/feedback route accepting category, description, optional session log and system info
  • Floating feedback button + modal in base.html (themed, works with all color schemes)
  • Session logging hooked into key routes: scan, process, apply_fix, reject_fix, apply_all, worker start/stop
  • Flask error handlers (500/404) that log to session buffer and suggest feedback on errors
  • Structured for future Skaldleita API forwarding (local storage for now)

Test plan

  • Click feedback button, verify modal opens with correct fields
  • Submit feedback in each category, verify stored in feedback.json
  • Verify session log captures actions (scan a book, check log)
  • Verify path sanitization strips real paths and API keys
  • Trigger a 500 error and confirm error handler logs it
  • Check modal theming in both default and skaldleita themes
  • Run test-naming-issues.py (281/281 pass)
  • ruff check --select=F821 clean

- Add library_manager/feedback.py: session action logger (circular buffer),
  path/data sanitizer, local feedback storage (feedback.json)
- Add POST /api/feedback route for submitting bug reports, corrections,
  feature requests with optional session log and system info
- Add floating feedback button + modal to base.html matching existing
  theme system (works with both default and skaldleita themes)
- Add Flask error handlers (500, 404) that log to session buffer
- Hook session logging into key routes: scan, process, apply_fix,
  reject_fix, worker start/stop, apply_all
- Feedback stored locally, structured for future Skaldleita API forwarding
- feedback.py: Add best-effort proxy to Skaldleita feedback API
  with signed request headers. Local storage remains primary.
  Add Docker detection to system info.

- base.html: Add error feedback modal that auto-appears on 500
  errors (via fetch interceptor detecting suggest_feedback flag).
  Users can describe what they were doing + send crash report.
@bucket-agent
Copy link

bucket-agent bot commented Feb 16, 2026

🔍 Vibe Check Review

Context

PR #158 adds a user feedback system with session logging, crash detection, and UI for submitting feedback with sanitized data.

Codebase Patterns I Verified

  • Logging: Uses logger = logging.getLogger(__name__) consistently (checked config.py, database.py)
  • Error handling: Specific exceptions preferred (e.g., except (json.JSONDecodeError, IOError)), bare except: only in migration code
  • Imports: from datetime import datetime is the pattern (found in app.py line 38)
  • API responses: jsonify({'success': False, 'error': 'message'}) is standard format
  • File I/O: Nested try/except for reading existing files before writing
  • Thread safety: Worker module uses locks for shared state
  • Path handling: pathlib.Path for all path operations

✅ Good

  • Thread-safe session log - Uses Lock() and deque properly for concurrent access
  • Sanitization patterns - Comprehensive regex for paths, IPs, and API keys (tested and working)
  • Bounded storage - MAX_FEEDBACK_ENTRIES and MAX_SESSION_LOG_SIZE prevent unbounded growth
  • Graceful degradation - File I/O wrapped in try/except, returns empty list on failure
  • Consistent patterns - Matches existing codebase style for logging, imports, error handling
  • 500 error handler - Logs crash to session buffer and suggests feedback via suggest_feedback: true
  • User privacy - Session log sanitizes paths automatically, checkboxes for opt-in data sharing
  • UI integration - Floating button, modal form, toast notifications follow Bootstrap patterns

🚨 Issues Found

Severity Location Issue Fix
HIGH feedback.py:150 Bare except Exception - Catches ALL exceptions including KeyboardInterrupt, MemoryError. File I/O could hide critical system errors. Use except (IOError, OSError) as e: to catch only file-related errors
HIGH app.py:8731 Missing CHANGELOG entry - This is a feat: PR adding user-visible features but CHANGELOG.md was not updated Add entry to CHANGELOG.md documenting the new feedback system
MEDIUM Scope alignment Skaldleita API integration NOT implemented - Issue #113 says "integrated with Skaldleita API" but code only stores locally with comment "structured for future Skaldleita API forwarding" Either: (1) Implement API forwarding, OR (2) Update issue to mark this as Phase 1 (local storage), with API integration as Phase 2
MEDIUM app.py:622 No crash auto-prompt on 500 errors - API returns suggest_feedback: true but no JavaScript handler listens for this flag to auto-show the modal Add error response handler in base.html to check for suggest_feedback and auto-open modal with pre-filled crash context
LOW feedback.py:37-39 API key pattern incomplete - Missing common patterns: AWS keys (AKIA*), GitHub tokens (ghp_, gho_), OpenAI org keys (org-*) Add patterns: AKIA[A-Z0-9]{16}, gh[ops]_[a-zA-Z0-9]{36,}, org-[a-zA-Z0-9]{24,}
LOW feedback.py:33-34 Path sanitization too greedy - [internal path] becomes [PATH][PATH] because both [internal path] and /audiobooks match separately Make patterns mutually exclusive or use longer patterns first: r'[internal path]"\']+(/[^/\s"\']+)*'

📋 Scope Verification

Issue Problem Addressed? Notes
#113 User feedback & crash reporting with Skaldleita API ⚠️ PARTIAL ✅ UI implemented
✅ Session logging (50 entries)
✅ Sanitization
✅ Categories (bug/correction/feature/other)
✅ Crash detection (500 handler logs error)
Skaldleita API integration missing (only local storage)
Auto-prompt on crash not working (flag returned but no JS handler)

Scope Status: SCOPE_PARTIAL

What's Missing:

  1. Skaldleita API forwarding - Issue says "integrated with Skaldleita API" but PR only stores locally
  2. Crash auto-prompt - Issue says "crash handler auto-prompts feedback" but 500 error doesn't actually trigger the modal

📝 Documentation Check

  • CHANGELOG.md: ❌ Missing - feat: PR must document user-visible changes
  • README.md: ⚠️ Consider updating - New feedback button is user-visible, should document in features section

🎯 Verdict

REQUEST_CHANGES

Required fixes:

  1. Change bare except Exception to specific exception types (feedback.py:150)
  2. Add CHANGELOG.md entry documenting the feedback system
  3. Clarify scope - Either implement Skaldleita API integration OR update issue/PR description to say this is Phase 1 (local only)
  4. Fix crash auto-prompt - Add JavaScript to detect suggest_feedback: true in API error responses and auto-open modal

Recommended (not blocking):

  • Improve API key pattern coverage (AWS, GitHub, OpenAI org keys)
  • Fix path sanitization double-replacement issue ([internal path][PATH][PATH])

Security Note: No critical vulnerabilities found. Sanitization works but could be more comprehensive.

@bucket-agent
Copy link

bucket-agent bot commented Feb 16, 2026

🔍 Vibe Check Review

Context

PR #158 adds a user feedback system with floating UI button, session logging, crash auto-prompt, and integration with Skaldleita API.

Codebase Patterns I Verified

Error Handling:

  • ✅ Codebase uses specific exceptions: (json.JSONDecodeError, IOError), (json.JSONDecodeError, OSError) in instance.py, config.py
  • ✅ Bare except: allowed for non-critical fallbacks (e.g., version detection in bookdb.py:48)
  • requests.exceptions.Timeout used in providers for timeout handling

Type Hints:

  • ✅ Consistently used in library_manager modules: def get_instance_id() -> str, def save_instance_data(data: dict) -> None
  • ✅ feedback.py follows this pattern with return type hints

Timeouts:

  • ✅ All requests calls in providers use timeout=10 (standard pattern: bookdb.py:145, audnexus.py:71)
  • ✅ feedback.py uses timeout=10 for Skaldleita proxy

Logging:

  • ✅ Follows standard pattern: logger = logging.getLogger(__name__) then logger.info(), logger.error(), logger.debug()
  • ✅ No print statements for production code

Security (XSS):

  • ✅ Uses .textContent for error messages (line 560 in diff), NOT .innerHTML - safe from XSS
  • ✅ Only .innerHTML usage is for static icon HTML strings - safe

✅ Good

  1. Security - Sanitization: Comprehensive path/IP/API key sanitization with multiple regex patterns (lines 205-213)
  2. XSS Protection: Correctly uses .textContent for user error messages, not .innerHTML
  3. Defensive - Thread Safety: Session log uses Lock() for thread-safe deque operations (lines 200-202, 249-250)
  4. Error Handling: All file operations wrapped in specific exception handlers (json.JSONDecodeError, IOError, OSError)
  5. Type Hints: Full type hints on all public functions matching codebase style
  6. Defensive - Timeouts: requests.post() has timeout=10 parameter (line 363)
  7. Defensive - Bounded Storage: Circular buffer with maxlen=50 for session log, MAX_FEEDBACK_ENTRIES=200 cap (lines 197-198, 322)
  8. Error Handling - Silent Fallback: Skaldleita proxy failure is best-effort, never crashes (lines 344-373, exception caught at line 371)
  9. Data Validation: Category validated against whitelist, defaults to 'other' (lines 125-127)
  10. CHANGELOG: Properly updated with detailed feature description

🚨 Issues Found

Severity Location Issue Fix
CRITICAL feedback.py:348 Import inside function breaks if module not loaded yet. from .signing import generate_signature will fail if signing.py hasn't been imported. Move imports to top of file or add error handling for ImportError
CRITICAL feedback.py:349 Import inside function. from .providers.bookdb import get_lm_version - same issue Move to top of file or handle ImportError
HIGH feedback.py:371 Bare except Exception is too broad for "best-effort" pattern. Should catch specific exceptions to avoid masking bugs. Change to except (requests.RequestException, ImportError, AttributeError, KeyError) as e:
MEDIUM feedback.py:267 Bare tb_module.format_exc() can include sensitive paths. Already sanitized on line 271, but traceback may have multiple path instances. Add comment explaining sanitization covers all traceback lines
LOW app.py:47 Imports feedback functions but never uses feedback_sanitize alias in the visible diff context Verify all imported functions are actually used, remove unused aliases

CRITICAL ISSUE DETAILS - Import Inside Function:

Lines 347-349 in feedback.py:

def _proxy_to_skaldleita(entry):
    """Forward feedback to Skaldleita API. Best-effort, never raises."""
    try:
        from .signing import generate_signature
        from .providers.bookdb import get_lm_version

Why this is critical: If signing or providers.bookdb modules fail to import (missing dependency, circular import, etc.), the function will raise ImportError but the bare except Exception on line 371 will silently swallow it. This is acceptable IF the code is truly "never raises", but it's better to explicitly handle ImportError to distinguish import failures from runtime errors.

Pattern in codebase: app.py has several import-fallback patterns (lines 119, 132, 144) that catch ImportError specifically.

📋 Scope Verification

Issue Problem Addressed? Notes
#113 User feedback and crash reporting system with Skaldleita integration ✅ COMPLETE All components implemented: UI button, feedback modal, crash auto-prompt, session logging (50 entries), sanitization, API proxy with HMAC signing, local storage in feedback.json

Scope Status: SCOPE_OK

All requirements met:

  • ✅ Feedback form categories: Bug Report, Correction, Feature Request, Other (lines 420-424)
  • ✅ Session log stores last 50 sanitized actions (lines 198, 226-250)
  • ✅ Crash handler auto-prompts feedback (lines 458-485, 556-609)
  • ✅ Feedback POST route proxies to Skaldleita with instance_id and version (lines 116-144, 344-373)
  • ✅ Path/API key sanitization (lines 216-223)
  • ✅ Local storage fallback (lines 310-342)

📝 Documentation Check

  • CHANGELOG.md: ✅ Updated (comprehensive description in Unreleased section)
  • README.md: ⚠️ Consider updating - New user-visible feature (feedback button), should mention in features list

🎯 Verdict

REQUEST_CHANGES

Required fixes before merge:

  1. CRITICAL: Fix imports in _proxy_to_skaldleita() function (feedback.py:347-349)

    • Option A: Move from .signing import generate_signature and from .providers.bookdb import get_lm_version to top of file
    • Option B: Explicitly catch ImportError separately:
    try:
        from .signing import generate_signature
        from .providers.bookdb import get_lm_version
    except ImportError as e:
        logger.debug(f"Cannot import signing/bookdb for feedback proxy: {e}")
        return False
  2. HIGH: Replace bare except Exception on line 371 with specific exceptions:

    except (requests.RequestException, ImportError, AttributeError, KeyError) as e:

Optional improvements:

  • Consider adding README.md mention of feedback feature in features list
  • Add comment on line 267 explaining that sanitization on line 271 covers all traceback lines

The implementation is solid overall with good security practices (sanitization, XSS protection, thread safety), but the import-inside-function pattern and broad exception handler need fixing before this can ship.

@bucket-agent
Copy link

bucket-agent bot commented Feb 16, 2026

🔍 Vibe Check Review

Context

PR #158 implements a user feedback and crash reporting system with a floating feedback button, session activity logging, crash auto-prompts, path/API key sanitization, and forwarding to Skaldleita API with local fallback storage.

Codebase Patterns I Verified

Checked existing patterns before judging:

  • Import style: Project uses top-level imports for standard library and third-party, then from library_manager.X import Y for internal modules (verified in library_manager/*.py)
  • Error handling: Project uses specific exceptions like (json.JSONDecodeError, IOError) not bare except: (verified in feedback.py:149, 162, instance.py:45, audio_tagging.py, etc.)
  • Logging: Uses logger = logging.getLogger(__name__) pattern consistently (verified in signing.py:26, instance.py:14, feedback.py:26)
  • Exception logging: Project uses except (SpecificException1, SpecificException2) as e: logger.error(f"...") pattern (verified in multiple files)
  • Network requests: Uses timeout parameter on requests.post (verified in feedback.py:192)
  • No existing errorhandlers: Grep found zero @app.errorhandler decorators before this PR - this is the first

✅ Good

  • Security-first: Comprehensive sanitization (paths, IPs, API keys) before storing or transmitting feedback
  • Thread-safe: Session log uses deque(maxlen=50) with Lock() for concurrent access
  • Defensive storage: Local storage ALWAYS succeeds, API proxy is best-effort - feedback never lost
  • Bounded storage: Limits feedback entries (200) and session log (50) to prevent file bloat
  • Specific exceptions: Uses (requests.RequestException, KeyError, AttributeError), (json.JSONDecodeError, IOError) - no bare excepts
  • Request timeout: 10s timeout on Skaldleita API call prevents hangs
  • Type hints: Good use on public functions (def get_instance_id() -> str:)
  • CHANGELOG updated: Properly documented in Unreleased section with issue reference
  • Clean UI integration: Floating button, modals, toast notifications - follows existing Bootstrap patterns

🚨 Issues Found

Severity Location Exact Code Quote Issue Fix
NONE - - - -

No issues found. Code is production-ready.

📋 Scope Verification

Issue Problem Addressed? Notes
#113 User feedback & crash reporting system All components implemented: feedback form with categories (Bug, Correction, Feature, Other), session log (50 sanitized actions), crash auto-prompt on 500 errors, sanitization (paths/IPs/keys), API proxy with local fallback

Scope Status: SCOPE_OK

All requirements met:

  • ✅ Feedback form with categories (bug, correction, feature, other)
  • ✅ Session activity log (50 sanitized entries)
  • ✅ Crash handler auto-prompts feedback on 500 errors
  • ✅ Path/API key/IP sanitization
  • ✅ Skaldleita API forwarding with HMAC signing
  • ✅ Local storage fallback (feedback.json)

📝 Documentation Check

  • CHANGELOG.md: ✅ Updated - Added comprehensive entry in [Unreleased] section
  • README.md: N/A - Feature is user-facing but self-explanatory through UI; README doesn't document individual features

🎯 Verdict

APPROVE

This PR is well-crafted and production-ready:

Security: Comprehensive sanitization protects user privacy (paths, IPs, API keys all redacted)

Robustness: Thread-safe logging, specific exception handling, timeouts on network calls, bounded storage, local-first with best-effort proxying

Code Quality: Matches project patterns, clean separation of concerns, proper error handling throughout

Scope: Fully addresses Issue #113 with all requested features

No changes required. Ship it.

@deucebucket deucebucket merged commit e0bf880 into develop Feb 16, 2026
3 checks passed
@deucebucket deucebucket deleted the feature/issue-113-feedback-system branch February 16, 2026 07:55
@bucket-agent
Copy link

bucket-agent bot commented Feb 16, 2026

🔍 Vibe Check Review

Context

PR #158 adds a comprehensive user feedback and crash reporting system with session logging, data sanitization, local storage, and best-effort proxying to Skaldleita API.

Codebase Patterns I Verified

Error Handling: Project uses specific exception types (e.g., requests.RequestException, json.JSONDecodeError, IOError) rather than bare except. Logging uses standard logger.error/info/debug.

Type Hints: Used consistently in library_manager modules (verified in instance.py: def get_instance_id() -> str:).

Imports: Standard library imports, then third-party (requests), then local imports with from .config.

Requests Pattern: Uses requests.post() with timeout parameters throughout the codebase (checked 20+ instances in app.py).

JavaScript Error Handling: Existing code uses .catch() blocks for fetch calls (verified 4+ instances in base.html).

Logging: Uses module-level logger = logging.getLogger(__name__) pattern consistently.

✅ Good

  • Excellent sanitization patterns: Comprehensive regex patterns for paths, IPs, and API keys with clear documentation
  • Thread-safe session logging: Proper use of Lock() for the circular buffer
  • Graceful degradation: Best-effort Skaldleita proxy with local storage as primary (feedback never lost)
  • Bounded storage: MAX_FEEDBACK_ENTRIES prevents unbounded growth
  • Proper HMAC signing: Uses existing generate_signature() infrastructure
  • Defensive error handling: Multiple try/except blocks with specific exception types
  • UI integration: Floating button, crash auto-prompt, and toast notifications are well-designed
  • Session log concept: Clever circular buffer approach for context capture
  • CHANGELOG and README updated: Version bumped, clear feature description

🚨 Issues Found

Severity Location Issue Fix
HIGH feedback.py:237-245 No input validation on sanitize_string() - If s is None, str(s) returns "None" which could leak into logs. Also no max length enforcement. Add: if not s: return "" and enforce max length: s = str(s)[:5000] before regex operations
HIGH feedback.py:336-340 Bare except on JSON decode - Catches json.JSONDecodeError AND IOError but if file is corrupted JSON, silently returns empty list. This could hide persistent corruption issues. Add logging: logger.warning(f"Failed to load feedback.json: {e}") inside the except block
HIGH feedback.py:398-402 Same issue - Silent failure on feedback retrieval Add same logging as above
MEDIUM feedback.py:289 Traceback extraction could fail - tb_module.format_exc() is called but if there's no active exception, returns "NoneType: None". The check on line 297 catches this but the string comparison is fragile. Use if tb_str and "Traceback" in tb_str: instead of checking for "NoneType: None"
MEDIUM feedback.py:213-214 Missing imports - Uses generate_signature and get_lm_version but imports are at top. Import order is correct but should verify these exist. Already correct - verified imports exist in signing.py and providers/bookdb.py
MEDIUM app.py:148 Feedback ID collision risk - Uses %Y%m%d-%H%M%S which could collide if two feedbacks submitted in same second from same instance. Change to: f"fb-{datetime.now().strftime('%Y%m%d-%H%M%S-%f')}-{get_instance_id()[-4:]}" (adds microseconds)
MEDIUM app.py:155 String truncation without encoding awareness - description[:2000] could split UTF-8 multibyte characters. Use .encode('utf-8')[:2000].decode('utf-8', errors='ignore') or just rely on JSON serialization to handle it
LOW feedback.py:269 Type inconsistency - result parameter accepts any string but only "success"/"error" seem meaningful. No validation. Add type hint: result: Optional[str] = None and docstring clarification
LOW base.html:619-621 fetch interceptor doesn't handle network errors - Only catches 500 status, but network failures won't trigger crash prompt. Add outer .catch() at line 627 after the fetch wrapper to handle network errors
LOW feedback.py:317 Hardcoded API URL - FEEDBACK_API_URL is hardcoded but BOOKDB_API_URL is imported from providers. Should be consistent. Import or derive from BOOKDB_API_URL: f"{BOOKDB_API_URL}/api/feedback"

📋 Scope Verification

Issue Problem Addressed? Notes
#113 Implement user feedback & crash reporting system with UI, session logging, crash detection, and API routes Fully implemented: feedback form with categories (bug/correction/feature/other), session log (50 entries), crash handler (500 error interceptor), sanitization (paths/IPs/keys), API route at /api/feedback, best-effort Skaldleita proxy

Key Requirements Met:

  • ✅ Feedback form categories: Bug Report, Correction, Feature Request, Other (lines 439-442 base.html)
  • ✅ Session log (50 entries): MAX_SESSION_LOG_SIZE = 50 (feedback.py:220)
  • ✅ Sanitized data: Path/IP/API key patterns (feedback.py:227-235)
  • ✅ Crash auto-prompt: 500 error handler + fetch interceptor (app.py:65-78, base.html:615-627)
  • ✅ API route: /api/feedback (app.py:136-165)
  • ✅ Skaldleita proxy with instance_id and version (feedback.py:366-392)

Scope Status: SCOPE_OK

📝 Documentation Check

  • CHANGELOG.md: ✅ Updated (0.9.0-beta.128 with detailed feature description)
  • README.md: ✅ Updated (version badge bumped to beta.128)

🎯 Verdict

REQUEST_CHANGES

Critical fixes required:

  1. feedback.py:237 - Add null check and max length to sanitize_string() before regex operations
  2. feedback.py:336-340, 398-402 - Add warning logs for JSON decode failures (silent corruption is dangerous)
  3. app.py:148 - Add microseconds to feedback_id to prevent collisions

Recommended improvements:
4. feedback.py:289 - Improve traceback detection logic (use "Traceback" check instead of "NoneType: None")
5. feedback.py:317 - Derive FEEDBACK_API_URL from BOOKDB_API_URL for consistency
6. base.html:627 - Add network error handler to fetch interceptor

The implementation is solid and well-architected, but the silent JSON failure handling and missing input validation on sanitize_string() are real risks that could hide issues in production. Fix these three critical items and this will be an excellent addition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant